/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 * Description: image_mbuf_video_sender head file
 */
#ifndef IMAGE_MBUF_VIDEO_SENDER_H
#define IMAGE_MBUF_VIDEO_SENDER_H

#include "image_sender_base.h"
#include "mdc/cam/camera/cameradecodedmbufserviceinterface_skeleton.h"
#include "util.h"
#include "core/types.h"
#include "driver/ascend_hal.h"
#include "securec.h"
#include "mdc/cam/camera/cameraimagembufserviceinterface_skeleton.h"
#include "camera_data_head.h"

#include <cstdint>
#include <getopt.h>
#include <pthread.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/time.h>
#include <sys/prctl.h>
#include <vector>
#include <unistd.h>

#include "acl.h"
#include "acl_rt.h"
#include "hi_dvpp.h"

#include "driver/ascend_hal.h"
#include "driver/ascend_hal_define.h"

constexpr int ENUM_NEGATIVE_TWO = -2;
constexpr int ENUM_NEGATIVE_ONE = -1;
constexpr int ENUM_0 = 0;
constexpr int ENUM_1 = 1;
constexpr int ENUM_2 = 2;
constexpr int ENUM_3 = 3;
constexpr int ENUM_4 = 4;
constexpr int ENUM_5 = 5;
constexpr int ENUM_6 = 6;
constexpr int ENUM_7 = 7;
constexpr int ENUM_8 = 8;
constexpr int ENUM_9 = 9;
constexpr int ENUM_10 = 10;
constexpr int ENUM_11 = 11;
constexpr int ENUM_12 = 12;
constexpr int ENUM_13 = 13;
constexpr int ENUM_14 = 14;
constexpr int ENUM_15 = 15;
constexpr int ENUM_16 = 16;
constexpr int ENUM_17 = 17;

constexpr int ENUM_FIVEK = 5000;
constexpr int ENUM_ONE_OO_TWO = 1002;
constexpr int ENUM_ONEK = 1000;
constexpr int ENUM_TWOK = 2000;
constexpr int ENUM_THREEK = 3000;

const int32_t MAX_NAME_LENG = 500;

struct GetImageFrameMsg {
    int32_t chanId = 0;
    uint8_t *inputFileBuf = nullptr;
    uint32_t *frameCount = nullptr;
    uint32_t fileSize = 0;
    hi_payload_type type = HI_PT_H264;
    uint8_t *dataDev = nullptr;
    int32_t ret = HI_SUCCESS;
    void *outBuffer = nullptr;
    uint32_t outBufferSize = 0;
    uint32_t outFormat = 0;

    int32_t i = 0;
    int32_t readLen = 0;
    uint8_t *bufPointer = NULL;
    bool isFindStart = false;
    bool isFindEnd = false;
};

struct InputImageMsg {
    FILE *fpInputFile = NULL;
    aclError aclRet = ACL_SUCCESS;
    uint32_t fileSize = 0;
    uint8_t *inputFileBuf = NULL;
    uint32_t readLen = 0;
    int32_t ret = HI_SUCCESS;
};

using CamPubImageMbufSkeleton = mdc::cam::camera::skeleton::CameraImageMbufServiceInterfaceSkeleton;
using MbufHandle = uintptr_t;

struct DealRunNode {
    uint32_t imageSize;
    uint32_t chanId;
    uint32_t outWidthStride;
    uint32_t outHeightStride;
};

class ImageMbufVideoSender : public ImageSenderBase<CamPubImageMbufSkeleton> {
    using PFunc = void *(*)(void *);
public:
    aclrtContext g_context = NULL;

public:
    ImageMbufVideoSender() = default;
    ImageMbufVideoSender(uint32_t id, cv::Size s, std::string dir)
        : ImageSenderBase<CamPubImageMbufSkeleton>(id, s, dir)
    {
        g_in_width = s.width;
        g_in_height = s.height;
        g_out_width = s.width;
        g_out_height = s.height;
        if (g_comPara.fileType == "h264") {
            g_in_format = 0;
        } else if (g_comPara.fileType == "h265") {
            g_in_format = 1;
        }
        g_out_format = 0;
        g_is_write_file = 1;
        g_out_width_stride = s.width;
        g_out_height_stride = s.height;
        sourceYuvPath = dir;
        if (dir.back() != '/') {
            sourceYuvPath += std::string("/") + "videoSource.yuv";
        } else {
            sourceYuvPath += "videoSource.yuv";
        }
        fp_yuv = fopen(sourceYuvPath.c_str(), "wb+");
    }
    ~ImageMbufVideoSender()
    {
        if (dataSkeleton != nullptr) {
            dataSkeleton->StopOfferService();
            dataSkeleton = nullptr;
        }
        if (handle != nullptr) {
            halBuffDeletePool(handle);
        }
    };

    bool RegisterSender()
    {
        std::cout << "Begin register MBUF image sender." << std::endl;
        auto result = ImageSenderBase<CamPubImageMbufSkeleton>::RegisterSender();
        std::cout << "Finished to register MBUF image sender." << std::endl;
        return result;
    }

    int32_t VdecDecoded()
    {
        std::string path = dir;
        std::string postfix = g_comPara.fileType;
        bool isLoop = g_comPara.loop;
        std::vector<std::string> files;
        int32_t fileCnt = getFilesBySort(path, files, g_comPara.fileType);
        if (fileCnt == ENUM_0) {
            return -1;
        }
        do {
            for (auto &file : files) {
                if (g_switchFlag == ENUM_NEGATIVE_TWO) {
                    file.clear();
                }
                if (file.empty()) {
                    return -1;
                }
                errno_t ret = memset_s(g_input_file_name, sizeof(g_input_file_name), 0, MAX_NAME_LENG);
                if (ret != EOK) {
                    return -1;
                }
                if (strcpy_s(g_input_file_name, file.length() + 1, file.c_str()) != 0) {
                    return -1;
                }
                VdecInit();
                InitThreadPara();
                VdecProcess();
                VdecUninit();
            }
        } while (isLoop);
        if (fclose(fp_yuv) != 0) {
        }
        return 0;
    }

    void InitThreadPara()
    {
        g_send_exit_state[VDEC_MAX_CHN_NUM] = {0};
        g_get_exit_state[VDEC_MAX_CHN_NUM] = {0};
        g_vdec_send_thread[VDEC_MAX_CHN_NUM] = {};
        g_vdec_get_thread[VDEC_MAX_CHN_NUM] = {};
        g_send_thread_id[VDEC_MAX_CHN_NUM] = {0};
        g_get_thread_id[VDEC_MAX_CHN_NUM] = {0};
    }
    int32_t VdecInit()
    {
        PrintParameter();
        int32_t ret = HI_SUCCESS;
        ret = HiDvppInit();
        if (ret != HI_SUCCESS) {
            return -1;
        }
        ret = VdecCreate();
        if (ret != HI_SUCCESS) {
            VdecDestroy();
            HiDvppDeinit();
            return -1;
        }
        InitOutbufferLock();
        GetStartTime();
        return ret;
    }
    void VdecUninit()
    {
        WaitVdecEnd();
        ShowDecodePerformance();
        DestroyOutbufferLock();
        VdecDestroy();
        HiDvppDeinit();
    }
    void VdecProcess()
    {
        int32_t ret = HI_SUCCESS;
        ret = CreateSendStreamThread();
        if (ret != 0) {
            StopSendStreamThread();
        } else {
            ret = CreateGetPicThread();
            if (ret != 0) {
                StopGetPicThread();
            }
        }
    }
    int32_t BufCreatePool(const uint32_t blkSize, const uint32_t blkNum)
    {
        constexpr uint32_t alignSize = 32U;
        mpAttr attr;
        attr.devid = 0;
        attr.mGroupId = 0;
        attr.blkSize = ((blkSize + alignSize) - 1U) & (~(alignSize - 1));
        attr.blkNum = blkNum;
        attr.align = alignSize;
        attr.hugePageFlag = 1U;
        const int32_t ret = halBuffCreatePool(&attr, &handle);
        if (ret != DRV_ERROR_NONE) {
            return -1;
        }
        return 0;
    }

    int32_t BufGetMbuf(poolHandle &handle, uintptr_t &getMbuf) const
    {
        Mbuf *pMbuf = nullptr;
        const int32_t ret = halMbufAllocByPool(handle, &pMbuf);
        if (ret != 0) {
            return -1;
        }
        getMbuf = reinterpret_cast<uintptr_t>(pMbuf);
        return 0;
    }

    int32_t GetMbuf(poolHandle &pHandle, MbufHandle &pMbuf)
    {
        return BufGetMbuf(pHandle, pMbuf);
    }

    bool PackCameraHead(const YuvImageInfo &info, const uintptr_t &head)
    {
        // 获取Mbuf指针及长度
        uintptr_t ptr;
        uint64_t size;
        const auto ret = GetMbufPtrLength(head, ptr, size);
        if (ret != 0) {
            return false;
        }
        const uint64_t headSize = sizeof(mdc::camera::CameraDataHead);
        if (headSize > size) {
            return false;
        }
        const auto pHead = reinterpret_cast<mdc::camera::CameraDataHead *>(ptr);
        if (memset_s(pHead, headSize, 0, headSize) != 0) {
            return false;
        }
        pHead->seq = info.header.Seq;
        pHead->height = info.height;
        pHead->width = info.width;
        pHead->ebdInfo.expoNumValid = true;
        pHead->ebdInfo.expoNum = 2U; // 2个曝光时间
        pHead->ebdInfo.shutterTimeValid = true;
        pHead->ebdInfo.shutterTime[0U] = info.header.Stamp.Shutter1;
        pHead->ebdInfo.shutterTime[1U] = info.header.Stamp.Shutter2;
        pHead->ebdInfo.expStartTimeStamp.timesec = info.header.Stamp.ExpStartS;
        pHead->ebdInfo.expStartTimeStamp.timensec = info.header.Stamp.ExpStartNs;
        pHead->ebdInfo.expEndTimeStamp.timesec = info.header.Stamp.ExpEndS;
        pHead->ebdInfo.expEndTimeStamp.timensec = info.header.Stamp.ExpEndNs;
        pHead->auxInfo.pubTimeStamp.isValid = true;
        pHead->auxInfo.pubTimeStamp.timesec = info.sendTimeHigh;
        pHead->auxInfo.pubTimeStamp.timensec = info.sendTimeLow;
        pHead->auxInfo.fsyncTimeStamp.isValid = true;
        pHead->auxInfo.fsyncTimeStamp.timesec = info.header.Stamp.Sec;
        pHead->auxInfo.fsyncTimeStamp.timensec = info.header.Stamp.Nsec;
        return true;
    }
    int32_t AppendMbuf(const uintptr_t head, const uintptr_t mbufPtr) const
    {
        return BufChainAppend(head, mbufPtr);
    }

    int32_t BufChainAppend(const uintptr_t head, const uintptr_t mbufPtr) const
    {
        Mbuf * const pHead = reinterpret_cast<Mbuf *>(head);
        Mbuf * const pMbuf = reinterpret_cast<Mbuf *>(mbufPtr);
        const int32_t ret = halMbufChainAppend(pHead, pMbuf);
        if (ret != 0) {
            return -1;
        }
        return 0;
    }

    int32_t GetMbufPtrLength(const MbufHandle &pMbuf, uintptr_t &ptr, uint64_t &len) const
    {
        if (BufGetDataPtr(pMbuf, ptr, len) != 0) {
            return -1;
        }
        return 0;
    }

    int32_t BufGetDataPtr(const uintptr_t in, uintptr_t &out, uint64_t &dataSize) const
    {
        Mbuf * const pSrc = reinterpret_cast<Mbuf *>(in);
        void *pData = nullptr;
        const int32_t ret = halMbufGetDataPtr(pSrc, &pData, &dataSize);
        if (ret != 0) {
            return -1;
        }
        out = reinterpret_cast<uintptr_t>(pData);
        return 0;
    }

    int32_t SetMbufPtr(const MbufHandle &pMbuf, const uint64_t &len) const
    {
        return BufSetDataLen(pMbuf, len);
    }

    int32_t BufSetDataLen(const uintptr_t setMbuf, const uint64_t len) const
    {
        Mbuf * const pMbuf = reinterpret_cast<Mbuf *>(setMbuf);
        const int32_t ret = halMbufSetDataLen(pMbuf, len);
        if (ret != 0) {
            return -1;
        }
        return 0;
    }

    int32_t BufFree(const uintptr_t in) const
    {
        Mbuf * const pSrc = reinterpret_cast<Mbuf *>(in);
        const int32_t ret = halMbufFree(pSrc);
        if (ret != 0) {
            return -1;
        }
        return 0;
    }

    void FillImageAndSend(std::string imgPath, uint32_t seq, int32_t *gStopFlag) override {}

public:
    int32_t GetOption(int32_t argc, char **argv);
    int32_t OptionSwitchAl(int32_t parameter);
    int32_t OptionSwitchMv(int32_t parameter);
    int32_t CheckOption();
    void CheckOptionFormat();
    int32_t PrintParameter();
    void VdecHandleSignal(int32_t signo);
    int32_t VdecCreate();
    int32_t VdecDestroy();
    int32_t CreateSendStreamThread();
    int32_t CreateGetPicThread();
    void StopSendStreamThread();
    void StopGetPicThread();
    static int32_t SendStream(ImageMbufVideoSender *thiz);
    static int32_t GetPic(ImageMbufVideoSender *thiz);
    int32_t GetEveryFrame(GetImageFrameMsg& frameMsg);
    int32_t DelayExec(uint64_t execTime, int32_t seconds);
    void WaitVdecEnd();
    int32_t ShowDecodePerformance();
    void GetStartTime();
    void InitOutbufferLock();
    void DestroyOutbufferLock();
    void ReleaseOutbuffer(uint32_t chanId);
    int32_t HiDvppInit();
    int32_t HiDvppDeinit();

private:
    poolHandle handle;
    float lastTime = 0.0F;
    const int32_t frameWidthTriploid = 3;
    const int32_t sleepTimeIntervalMs { 1000000 };
    char g_input_file_name[MAX_NAME_LENG] = "test264.h264";
    char g_output_file_name[MAX_NAME_LENG] = "./outfile";
    uint32_t g_in_width = 3840;  // input stream width
    uint32_t g_in_height = 2160; // input stream height
    uint32_t g_in_format = 0;
    uint32_t g_in_bitwidth = 8;
    uint32_t g_out_width = 0;
    uint32_t g_out_height = 0;
    uint32_t g_out_format = 0;
    uint32_t g_out_width_stride = 4096;
    uint32_t g_out_height_stride = 4096;
    uint32_t g_is_write_file = 1;
    uint32_t g_chn_num = 1;
    uint32_t g_ref_frame_num = 8;
    uint32_t g_display_frame_num = 2;
    uint32_t g_output_order = 0;
    uint32_t g_send_times = 1;
    uint32_t g_send_interval = 0;
    uint32_t g_delay_time = 1;
    uint32_t g_alloc_num = 20;
    uint32_t g_start_chn_num = 0;
    uint32_t g_render = 0;
    uint32_t g_exit = 0;

    uint32_t g_chan_create_state[VDEC_MAX_CHN_NUM] = {0};
    uint64_t g_start_time = 0;

    std::vector<void *> g_out_buffer_pool[VDEC_MAX_CHN_NUM];
    pthread_mutex_t g_out_buffer_pool_lock[VDEC_MAX_CHN_NUM];

    uint32_t g_send_exit_state[VDEC_MAX_CHN_NUM] = {0};
    uint32_t g_get_exit_state[VDEC_MAX_CHN_NUM] = {0};

    std::thread g_vdec_send_thread[VDEC_MAX_CHN_NUM] = {};
    std::thread g_vdec_get_thread[VDEC_MAX_CHN_NUM] = {};
    uint32_t g_send_thread_id[VDEC_MAX_CHN_NUM] = {0};
    uint32_t g_get_thread_id[VDEC_MAX_CHN_NUM] = {0};

    uint8_t *g_frame_addr[VDEC_MAX_CHN_NUM][9999];
    uint64_t g_frame_len[VDEC_MAX_CHN_NUM][9999];
    uint64_t g_vdec_start_time[VDEC_MAX_CHN_NUM] = {0};
    uint64_t g_vdec_end_time[VDEC_MAX_CHN_NUM] = {0};
    uint64_t g_vdec_get_frame_cnt[VDEC_MAX_CHN_NUM] = {0};

    aclrtRunMode g_run_mode = ACL_HOST;
    std::string sourceYuvPath;
    FILE *fp_yuv;
    std::chrono::high_resolution_clock::time_point sec;
    int32_t seq { 0 };
    DealRunNode dealRunNode;

private:
    int32_t InitGrp(void);
    void GetCurrentTimeUs(uint64_t &timeUs);
    int32_t SaveYuvFile(FILE * const fd, hi_video_frame frame, uint32_t chanId);
    int32_t SaveRgbFileAclHost(hi_video_frame frame, uint8_t *outImageBuf, FILE * const fd, uint32_t chanId);
    int32_t SaveYuvFileAclHost(hi_video_frame frame, uint8_t *outImageBuf, FILE * const fd, uint32_t chanId);
    bool DealRunNode(uint8_t *addr, hi_video_frame frame, FILE * const fd);
    static int32_t GetPicHost(hi_video_frame_info& frame, ImageMbufVideoSender *thiz,
        GetImageFrameMsg& frameMsg, hi_vdec_stream& stream, int32_t decResult);
    static void UpdateOutPicInfo(hi_vdec_pic_info &outPicInfo, ImageMbufVideoSender *thiz,
        GetImageFrameMsg& frameMsg, hi_vdec_stream& stream, uint32_t* pReadCount);
    int32_t GetFrameHost(uint8_t * const inputFileBuf, int32_t chanId, uint32_t fileSize, hi_payload_type type);
    static uint32_t DealSendTime(hi_vdec_pic_info &outPicInfo, ImageMbufVideoSender *thiz, GetImageFrameMsg& frameMsg,
        uint32_t sendFrameFailCnt, hi_vdec_stream& stream);
    static void DealSendFormat(ImageMbufVideoSender *thiz, GetImageFrameMsg& frameMsg, FILE *fpInputFile);
    static uint8_t *DealDataDev(ImageMbufVideoSender *thiz, uint8_t* dataDev, FILE *fpInputFile,
        GetImageFrameMsg& frameMsg);
    static void DealInput(ImageMbufVideoSender *thiz, InputImageMsg& inputMsg);
    int32_t DealGetFrameHost(uint8_t * const inputFileBuf, int32_t chanId, uint32_t fileSize, hi_payload_type type);
    int32_t FillYuvAndSend(uint8_t *ptr, int32_t w, int32_t h);
    bool DealMbufPtr(uintptr_t &mbufTmp, uint64_t actualLen, YuvImageInfo &yuvImageInfo, uintptr_t &head);
    int32_t SaveRgbFile(FILE *const fd, hi_video_frame frame, uint32_t chanId);
    int32_t VdecResetChn(uint32_t chanId);
    int32_t DealParam(uint32_t i);
    static int32_t DealDecResult(int32_t *decResult, int32_t *successCnt, uint32_t chanId, int32_t* failCnt);
    void GetEveryFrameH264(GetImageFrameMsg& frameMsg);
    void GetEveryFrameH265(GetImageFrameMsg& frameMsg);
    void Cof1();
    void Cof2();
    void Cof3();
    void Cof4();
    void Cof5();
    void Cof6();
    void Cof7();
    void Cof8();
    void Cof9();
    void Cof10();
    void Cof11();
    void Cof12();
    void Cof13();
    void Cof14();
    void Cof15();
    void Cof16();
    void Cof17();
    int32_t CofOther();
};

#endif // IMAGE_MBUF_VIDEO_SENDER_H
